<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>实时视频音频聊天</title>
    <style>
        /* 基础样式 */
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            box-sizing: border-box;
            background-color: #f0f2f5;
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100vh;
            overflow: hidden;
        }

        h1 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }

        .videos-container {
            position: relative;
            width: 100%;
            max-width: 1200px; /* 增大最大宽度以增强沉浸感 */
            margin: 0 auto 20px auto;
            background-color: #000;
            border: 2px solid #ccc;
            border-radius: 5px;
            overflow: hidden;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            transition: all 0.3s ease;
        }

        /* 帧率统计 */
        #fpsCounter, #sendFpsCounter, #desiredFpsCounter {
            position: absolute;
            background-color: rgba(0, 0, 0, 0.6);
            color: #0f0;
            padding: 5px 10px;
            border-radius: 5px;
            font-size: 0.9em;
            z-index: 10;
            backdrop-filter: blur(5px);
        }

        #fpsCounter {
            top: 10px;
            left: 10px;
        }

        #sendFpsCounter {
            top: 35px;
            left: 10px;
        }

        #desiredFpsCounter {
            top: 60px;
            left: 10px;
            background-color: rgba(255, 165, 0, 0.8);
            color: #fff;
        }

        /* 远端视频Canvas */
        #remoteCanvas {
            width: 100%;
            height: auto;
            background-color: #000;
            display: block;
        }

        /* 本地视频 */
        #localVideo {
            position: absolute;
            top: 10px;
            right: 10px;
            width: 160px;
            height: auto;
            background-color: #000;
            border: 2px solid #fff;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0,0,0,0.5);
            z-index: 5;
        }

        @media (max-width: 700px) {
            #localVideo {
                width: 120px;
                top: 5px;
                right: 5px;
            }
        }

        /* 控制区域 */
        .controls {
            display: flex;
            flex-direction: column;
            gap: 20px;
            max-width: 1200px; /* 同步最大宽度 */
            width: 100%;
            margin: 0 auto;
            background-color: #fff;
            padding: 25px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
            overflow-y: auto;
        }

        .controls div {
            display: flex;
            flex-direction: column;
        }

        .controls label {
            font-size: 1.1em;
            margin-bottom: 8px;
            color: #555;
        }

        .controls select, .controls input {
            padding: 10px;
            font-size: 1em;
            width: 100%;
            box-sizing: border-box;
            border: 1px solid #ccc;
            border-radius: 4px;
            transition: border-color 0.3s;
        }

        .controls select:focus, .controls input:focus {
            border-color: #28a745;
            outline: none;
        }

        /* 动作按钮区域 */
        .action-buttons {
            display: flex;
            justify-content: space-between;
            flex-wrap: wrap;
            gap: 15px;
            margin-top: 10px;
        }

        /* 按钮样式 */
        .action-buttons button {
            flex: 1 1 calc(32% - 15px); /* 三个按钮在一行 */
            padding: 12px;
            font-size: 1em;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.3s, transform 0.1s;
            color: white;
            min-width: 100px;
        }

        .action-buttons button:hover {
            transform: scale(1.05);
        }

        /* 按钮图标与标签 */
        .action-buttons button .icon {
            margin-right: 8px;
            font-size: 1.2em;
        }

        /* 切换按钮颜色 */
        .action-buttons button.active {
            background-color: #28a745; /* 绿色表示开启 */
        }

        .action-buttons button.inactive {
            background-color: #6c757d; /* 灰色表示关闭 */
        }

        /* 启动和重新连接按钮 */
        #startButton {
            background-color: #28a745;
            flex: 1 1 100%;
        }

        #reconnectButton {
            background-color: #ffc107;
            flex: 1 1 100%;
            color: #333;
        }

        /* 全屏按钮 */
        #fullscreenButton {
            background-color: #007bff; /* 蓝色表示全屏 */
            flex: 1 1 100%;
        }

        /* 状态显示 */
        .status {
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 20px auto 0 auto;
            font-size: 1.1em;
            color: #555;
            max-width: 1200px;
            width: 100%;
            background-color: #fff;
            padding: 15px 20px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }

        .status-icon {
            margin-right: 10px;
            font-size: 1.5em;
        }

        /* 全屏模式下的样式调整 */
        .fullscreen .videos-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            max-width: none;
            border-radius: 0;
            z-index: 1000;
        }

        .fullscreen .controls {
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            max-width: none;
            padding: 15px;
            border-radius: 0;
            box-shadow: 0 -2px 10px rgba(0,0,0,0.3);
            background-color: rgba(255, 255, 255, 0.9);
        }

        /* 媒体查询优化按钮布局 */
        @media (max-width: 900px) {
            .action-buttons button {
                flex: 1 1 48%; /* 两个按钮在一行 */
            }
        }

        @media (max-width: 600px) {
            .action-buttons button {
                flex: 1 1 100%;
                min-width: unset;
            }
        }
    </style>
</head>
<body>
<h1 id="tid">实时视频音频聊天</h1>
<div class="videos-container" id="videosContainer">
    <div id="fpsCounter">0 fps</div>
    <div id="sendFpsCounter">0 send/s</div>
    <div id="desiredFpsCounter">目标上传帧率: 30 fps</div>
    <canvas id="remoteCanvas"></canvas>
    <video id="localVideo" autoplay playsinline muted></video>
</div>

<div class="controls" id="controls">
    <div class="action-buttons">
        <button id="startButton">
            <span class="icon">▶️</span>启动聊天
        </button>
        <button id="reconnectButton" style="display: none;">
            <span class="icon">🔄</span>重新连接
        </button>
        <button id="fullscreenButton">
            <span class="icon">⛶</span>全屏
        </button>
    </div>
    <div>
        <label for="cameraSelect">选择摄像头:</label>
        <select id="cameraSelect"></select>
    </div>
    <div>
        <label for="microphoneSelect">选择麦克风:</label>
        <select id="microphoneSelect"></select>
    </div>
    <div>
        <label for="ipInput">WebSocket服务器IP地址:</label>
        <input type="text" id="ipInput" value="47.98.150.92" />
    </div>
    <div>
        <label for="idInput">我的ID:</label>
        <div style="display: flex; position: relative;">
            <input type="text" id="idInput" value="111" style="flex: 1; margin-right: 10px;"/>
        </div>
    </div>
    <div>
        <label for="toIdInput">我想看谁:</label>
        <input type="text" id="toIdInput" value="222"/>
    </div>
    <div class="action-buttons">
        <button id="toggleMicrophoneButton" class="inactive">
            <span class="icon">🔇</span>麦克风已关闭
        </button>
        <button id="toggleVideoButton" class="inactive">
            <span class="icon">🎥</span>视频已关闭
        </button>
        <button id="muteButton" class="inactive">
            <span class="icon">🔊</span>静音
        </button>
    </div>
</div>

<div class="status" id="status">
    <span class="status-icon" id="statusIcon">🔴</span>
    <span id="statusText">未连接</span>
</div>

<script>
    // 状态变量
    let isConnecting = false;
    let isConnected = false;
    let isRemoteVideoPaused = false;
    let isRemoteAudioMuted = false;
    let isLocalVideoPaused = false;
    let isLocalAudioPaused = false;

    const localVideo = document.getElementById('localVideo');
    const remoteCanvas = document.getElementById('remoteCanvas');
    const remoteContext = remoteCanvas.getContext('2d');
    const cameraSelect = document.getElementById('cameraSelect');
    const microphoneSelect = document.getElementById('microphoneSelect');
    const startButton = document.getElementById('startButton');
    const reconnectButton = document.getElementById('reconnectButton');
    const ipInput = document.getElementById('ipInput');
    const idInput = document.getElementById('idInput');
    const toIdInput = document.getElementById('toIdInput');
    const statusDisplay = document.getElementById('statusText');
    const statusIcon = document.getElementById('statusIcon');
    const fpsCounter = document.getElementById('fpsCounter');
    const sendFpsCounter = document.getElementById('sendFpsCounter');
    const desiredFpsCounter = document.getElementById('desiredFpsCounter');
    const muteButton = document.getElementById('muteButton');
    const toggleMicrophoneButton = document.getElementById('toggleMicrophoneButton');
    const toggleVideoButton = document.getElementById('toggleVideoButton');
    const fullscreenButton = document.getElementById('fullscreenButton');
    const videosContainer = document.getElementById('videosContainer');
    const controls = document.getElementById('controls');

    let localStream = null;
    let socketVideo = null;
    let socketAudio = null;
    let audioContext = null;
    let audioSource = null;
    let scriptProcessor = null;
    let retryCountVideo = 0;
    let retryCountAudio = 0;
    const maxRetries = 9999999999999; // 最大重试次数，避免无限重试
    const baseRetryInterval = 1000; // 基础重试间隔（毫秒）
    const maxRetryInterval = 5000; // 最大重试间隔为10秒
    let  isUploadAllowed = false;

    // 帧率统计变量
    let frameCount = 0;
    let fps = 0;

    // 发送帧率统计变量
    let sendFrameCount = 0;
    let sendFps = 0;

    // 使用 requestAnimationFrame 的帧发送控制
    let sendFrameRequestId = null;

    // 每秒更新一次帧率
    setInterval(() => {
        fps = frameCount;
        frameCount = 0;
        fpsCounter.textContent = fps + ' fps';
    }, 1000);

    // 每秒更新一次发送帧率
    setInterval(() => {
        sendFps = sendFrameCount;
        sendFrameCount = 0;
        sendFpsCounter.textContent = sendFps + ' send/s';
    }, 1000);

    // 定义目标上传帧率变量
    let desiredSendFps = 30; // 初始目标上传帧率
    const maxDesiredFps = 60; // 最大目标上传帧率
    const minDesiredFps = 10;  // 最低目标上传帧率调整为10 fps
    let lastSendTime = 0;     // 上一次发送帧的时间戳

    // 每五秒调整上传帧率
    setInterval(() => {
        if (sendFps > fps) {
            // 如果上传帧率大于渲染速度，则将目标上传帧率设置为渲染速度
            desiredSendFps = Math.max(fps, minDesiredFps);
        } else {
            // 如果上传帧率小于或等于渲染速度，则增加目标上传帧率
            desiredSendFps = Math.min(desiredSendFps + 5, maxDesiredFps);
        }
        desiredFpsCounter.textContent = `目标上传帧率: ${desiredSendFps} fps`;
        console.log(`调整后的目标上传帧率: ${desiredSendFps} fps`);
    }, 5000);

    // 获取所有媒体设备
    async function getAndPopulateDevices() {
        try {
            const devices = await navigator.mediaDevices.enumerateDevices();
            // 清空之前的选项
            cameraSelect.innerHTML = '';
            microphoneSelect.innerHTML = '';

            devices.forEach(device => {
                const option = document.createElement('option');
                option.value = device.deviceId;
                option.text = device.label || `${device.kind} ${cameraSelect.length + 1}`;
                if (device.kind === 'videoinput') {
                    cameraSelect.appendChild(option);
                } else if (device.kind === 'audioinput') {
                    microphoneSelect.appendChild(option);
                }
            });

            // 设置默认选择
            if (cameraSelect.options.length > 0) {
                cameraSelect.selectedIndex = 0;
            }
            if (microphoneSelect.options.length > 0) {
                microphoneSelect.selectedIndex = 0;
            }
        } catch (error) {
            console.error('无法获取设备列表:', error);
            updateStatus('无法获取设备列表。请检查浏览器权限或设备连接。', 'error');
        }
    }

    // 启动聊天
    const startChat = async () => {
        const selectedCameraId = cameraSelect.value;
        const selectedMicrophoneId = microphoneSelect.value;
        const chatId = idInput.value.trim() || '111';
        const toId = toIdInput.value.trim() || '222';

        // 防止多次启动
        if (isConnecting || isConnected) {
            console.warn('聊天已启动或正在启动，无法重复启动。');
            return;
        }

        isConnecting = true;
        updateStatus('正在连接...', 'connecting');
        startButton.disabled = true;
        reconnectButton.style.display = 'none';
        toggleMicrophoneButton.disabled = true;
        toggleVideoButton.disabled = true;
        muteButton.disabled = true;
        fullscreenButton.disabled = true;

        // 停止当前流（如果有）
        if (localStream) {
            localStream.getTracks().forEach(track => track.stop());
            localStream = null;
        }

        // 关闭已有的 WebSocket 连接（如果有）
        if (socketVideo) {
            socketVideo.close();
            socketVideo = null;
        }
        if (socketAudio) {
            socketAudio.close();
            socketAudio = null;
        }

        // 取消已有的发送帧定时器
        if (sendFrameRequestId) {
            cancelAnimationFrame(sendFrameRequestId);
            sendFrameRequestId = null;
        }

        // 清理音频相关资源
        if (audioContext) {
            audioContext.close();
            audioContext = null;
        }

        try {
            // 获取选定摄像头和麦克风的媒体流
            localStream = await navigator.mediaDevices.getUserMedia({
                video: { deviceId: selectedCameraId ? { exact: selectedCameraId } : undefined }, // 保持原始分辨率
                audio: { deviceId: selectedMicrophoneId ? { exact: selectedMicrophoneId } : undefined }
            });
            localVideo.srcObject = localStream;

            // 获取设备列表，此时已获得权限，可以获取设备标签
            await getAndPopulateDevices();

            const wsIp = ipInput.value.trim() || window.location.hostname;
            const chatIdEncoded = encodeURIComponent(chatId);
            const toIdEncoded = encodeURIComponent(toId);

            // 初始化 WebSocket 连接（视频）
            connectWebSocketVideo(`ws://${wsIp}:2222/ws/video?id=${chatIdEncoded}&toId=${toIdEncoded}`);

            // 初始化 WebSocket 连接（音频）
            connectWebSocketAudio(`ws://${wsIp}:2222/ws/audio?id=${chatIdEncoded}&toId=${toIdEncoded}`);

            // 创建 AudioContext
            audioContext = new (window.AudioContext || window.webkitAudioContext)();

            // 确保 AudioContext 已启动
            if (audioContext.state === 'suspended') {
                await audioContext.resume();
            }

            // 发送视频帧
            const offscreenCanvas = document.createElement('canvas');
            const offscreenCtx = offscreenCanvas.getContext('2d');
            offscreenCanvas.width = 640; // 增加分辨率以匹配更大屏幕
            offscreenCanvas.height = 480; // 增加分辨率以匹配更大屏幕

            const sendVideoFrame = async (timestamp) => {
                if (socketVideo && socketVideo.readyState === WebSocket.OPEN && !isLocalVideoPaused) {
                    // 计算时间间隔
                    const interval = 1000 / desiredSendFps;
                    if (timestamp - lastSendTime >= interval) {
                        lastSendTime = timestamp;

                        // 画到离屏Canvas
                        offscreenCtx.drawImage(localVideo, 0, 0, offscreenCanvas.width, offscreenCanvas.height);

                        try {
                            const blob = await new Promise(resolve => {
                                offscreenCanvas.toBlob(resolve, 'image/jpeg',0.1);
                            });

                            if (blob) {
                                const arrayBuffer = await blob.arrayBuffer();
                                if (socketVideo && socketVideo.readyState === WebSocket.OPEN && isUploadAllowed) {
                                    socketVideo.send(arrayBuffer);
                                    sendFrameCount++;
                                }
                            }
                        } catch (error) {
                            console.error('发送视频帧时出错:', error);
                            updateStatus('发送视频帧失败，请检查网络或服务器状态。', 'error');
                        }
                    }
                }

                // 继续下一帧
                sendFrameRequestId = requestAnimationFrame(sendVideoFrame);
            };

            // 启动帧发送循环
            sendFrameRequestId = requestAnimationFrame(sendVideoFrame);

            // 发送音频数据
            audioSource = audioContext.createMediaStreamSource(localStream);
            const bufferSize = 4096; // 调整缓冲区大小
            scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);

            scriptProcessor.onaudioprocess = (audioEvent) => {
                if (socketAudio && socketAudio.readyState === WebSocket.OPEN && !isLocalAudioPaused && isUploadAllowed) {
                    const inputBuffer = audioEvent.inputBuffer.getChannelData(0);
                    // 压缩音频数据以减少传输量
                    const compressedBuffer = compressFloat32Array(inputBuffer);
                    socketAudio.send(compressedBuffer.buffer);
                    sendFrameCount++; // 视音频为帧计数
                }
            };

            audioSource.connect(scriptProcessor);
            scriptProcessor.connect(audioContext.destination);

            isConnected = true;
            isConnecting = false;
            updateStatus('已连接', 'connected');
            startButton.disabled = true;
            reconnectButton.style.display = 'none';
            toggleMicrophoneButton.disabled = false;
            toggleVideoButton.disabled = false;
            muteButton.disabled = false;
            fullscreenButton.disabled = false;

            // 设置按钮状态为激活
            setToggleButtonsState(true);



            // 定时检查是否允许上传音频
            const checkUploadPermission = () => {
                fetch(`http://${wsIp}:8080/isUpload?id=${chatIdEncoded}`)
                    .then(response => response.json()) // 假设返回的是 JSON 格式
                    .then(data => {
                        if (data > 0) {
                            isUploadAllowed = true; // 如果服务器返回true，允许上传
                            console.log(" upload allowed. true");
                            document.title = "实时视频音频聊天 (当前观看人数 "+data+"/人)";
                            document.getElementById("tid").innerHTML  = "实时视频音频聊天 (当前观看人数 "+data+"/人)";
                        } else {
                            isUploadAllowed = false; // 否则，不允许上传
                            document.title = "实时视频音频聊天 (暂无人员观看)";
                            document.getElementById("tid").innerHTML  = "实时视频音频聊天 (暂无人员观看)";
                            console.log(" upload not allowed. false 不允许上传");
                        }
                    })
                    .catch(error => {
                        isUploadAllowed = false; // 否则，不允许上传
                        document.title = "实时视频音频聊天 (暂无人员观看)";
                        document.getElementById("tid").innerHTML  = "实时视频音频聊天 (暂无人员观看)";
                        console.error("Error checking upload permission:", error);
                    });
            };

            // 定时器：每5秒检查一次
            setInterval(checkUploadPermission, 2000);
        } catch (error) {
            console.error('启动聊天时出错:', error);
            updateStatus('启动聊天失败，请检查设备权限和网络连接。', 'error');
            isConnecting = false;
            startButton.disabled = false;
            reconnectButton.style.display = 'block';
            fullscreenButton.disabled = true;
        }
    };

    // 压缩 Float32Array 为 Int16Array (优化)
    function compressFloat32Array(float32Array) {
        const int16Array = new Int16Array(float32Array.length);
        for (let i = 0; i < float32Array.length; i++) {
            let s = Math.max(-1, Math.min(1, float32Array[i]));
            int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
        }
        return int16Array;
    }

    // 解压缩 Int16Array 为 Float32Array (优化)
    function decompressInt16Array(int16Array) {
        const float32Array = new Float32Array(int16Array.length);
        for (let i = 0; i < int16Array.length; i++) {
            float32Array[i] = int16Array[i] / 0x7FFF;
        }
        return float32Array;
    }

    // 更新状态显示和图标
    function updateStatus(message, status = 'info') {
        statusDisplay.textContent = message;
        switch (status) {
            case 'connecting':
                statusIcon.textContent = '⏳'; // 沙漏图标
                break;
            case 'connected':
                statusIcon.textContent = '✅'; // 绿色勾
                break;
            case 'error':
                statusIcon.textContent = '❌'; // 红色叉
                break;
            case 'info':
                statusIcon.textContent = '🔵'; // 蓝色圆点
                break;
            default:
                statusIcon.textContent = '🔴'; // 默认红色圆点
        }
    }

    // WebSocket 重试逻辑
    function connectWebSocketVideo(url) {
        if (socketVideo) {
            socketVideo.close();
            socketVideo = null;
        }

        socketVideo = new WebSocket(url);
        socketVideo.binaryType = 'arraybuffer';

        socketVideo.onopen = () => {
            console.log('视频 WebSocket连接已打开');
            updateStatus('视频连接已建立', 'connected');
            retryCountVideo = 0; // 重置重试计数器
            // 如果已连接音频，则设置视频按钮为激活
            if (isConnected) {
                toggleVideoButton.classList.remove('inactive');
                toggleVideoButton.classList.add('active');
                toggleVideoButton.innerHTML = `<span class="icon">🎥</span> 视频已开启`;
            }
        };

        socketVideo.onerror = (error) => {
            console.error('视频 WebSocket错误:', error);
            handleWebSocketError('视频', error);
        };

        socketVideo.onclose = (event) => {
            console.log(`视频 WebSocket连接已关闭，代码: ${event.code}，原因: ${event.reason}`);
            handleWebSocketClose('视频', event);
        };

        // 接收视频帧并显示在 canvas 上
        socketVideo.onmessage = async (event) => {
            if (isRemoteVideoPaused) return; // 如果远端视频被暂停，不处理

            const blob = new Blob([event.data], { type: 'image/jpeg' });
            try {
                const imgBitmap = await createImageBitmap(blob);

                // 设置 canvas 尺寸以匹配图像并进行高清放大
                const scaleFactor = 2; // 放大倍数
                if (!remoteCanvas.dataset.initialized) {
                    remoteCanvas.width = imgBitmap.width * scaleFactor;
                    remoteCanvas.height = imgBitmap.height * scaleFactor;
                    remoteCanvas.dataset.initialized = 'true';
                }

                // 清空 canvas
                remoteContext.clearRect(0, 0, remoteCanvas.width, remoteCanvas.height);

                // 绘制放大后的图像
                remoteContext.drawImage(imgBitmap, 0, 0, imgBitmap.width * scaleFactor, imgBitmap.height * scaleFactor);
                frameCount++;

                // 释放 ImageBitmap
                imgBitmap.close();
            } catch (error) {
                console.error("加载图像时出错:", error);
                updateStatus('加载远端视频帧失败，请检查网络或服务器状态。', 'error');
            }
        };
    }

    function connectWebSocketAudio(url) {
        if (socketAudio) {
            socketAudio.close();
            socketAudio = null;
        }

        socketAudio = new WebSocket(url);
        socketAudio.binaryType = 'arraybuffer';

        socketAudio.onopen = () => {
            console.log('音频 WebSocket连接已打开');
            updateStatus('音频连接已建立', 'connected');
            retryCountAudio = 0; // 重置重试计数器
            // 如果已连接视频，则设置音频按钮为激活
            if (isConnected) {
                toggleMicrophoneButton.classList.remove('inactive');
                toggleMicrophoneButton.classList.add('active');
                toggleMicrophoneButton.innerHTML = `<span class="icon">🎤</span> 麦克风已开启`;
            }
        };

        socketAudio.onerror = (error) => {
            console.error('音频 WebSocket错误:', error);
            handleWebSocketError('音频', error);
        };

        socketAudio.onclose = (event) => {
            console.log(`音频 WebSocket连接已关闭，代码: ${event.code}，原因: ${event.reason}`);
            handleWebSocketClose('音频', event);
        };

        // 接收音频数据并播放
        socketAudio.onmessage = (event) => {
            if (isRemoteAudioMuted) return; // 如果远端音频被静音，不处理

            const arrayBuffer = event.data;
            if (!audioContext) return;

            try {
                // 解压缩音频数据
                const int16Array = new Int16Array(arrayBuffer);
                const float32Array = decompressInt16Array(int16Array);
                const audioBuffer = audioContext.createBuffer(1, float32Array.length, audioContext.sampleRate);
                audioBuffer.copyToChannel(float32Array, 0);

                playAudioBuffer(audioBuffer);
            } catch (error) {
                console.error('解码音频数据失败:', error);
                updateStatus('解码远端音频数据失败，请检查网络或服务器状态。', 'error');
            }
        };
    }

    const playAudioBuffer = (audioBuffer) => {
        const source = audioContext.createBufferSource();
        source.buffer = audioBuffer;
        source.connect(audioContext.destination);
        source.start();
    };

    // 处理 WebSocket 错误
    function handleWebSocketError(type, error) {
        if (!navigator.onLine) {
            updateStatus(`${type}连接失败：网络断开。请检查您的网络连接。`, 'error');
        } else {
            updateStatus(`${type}连接错误。请检查服务器状态或尝试重新连接。`, 'error');
        }
    }

    // 处理 WebSocket 关闭
    function handleWebSocketClose(type, event) {
        if ((type === '视频' && retryCountVideo >= maxRetries) ||
            (type === '音频' && retryCountAudio >= maxRetries)) {
            updateStatus(`${type}连接失败，请检查服务器或网络。`, 'error');
            reconnectButton.style.display = 'block';
            startButton.disabled = false;
            toggleMicrophoneButton.disabled = true;
            toggleVideoButton.disabled = true;
            muteButton.disabled = true;
            fullscreenButton.disabled = true;
            isConnected = false;
            isConnecting = false;
            return;
        }

        let retryCount = type === '视频' ? retryCountVideo : retryCountAudio;
        const newRetryCount = retryCount + 1;
        const retryInterval = Math.min(baseRetryInterval * Math.pow(2, retryCount), maxRetryInterval);

        if (type === '视频') {
            retryCountVideo = newRetryCount;
        } else {
            retryCountAudio = newRetryCount;
        }

        console.log(`尝试重新连接 ${type} WebSocket in ${retryInterval / 1000} 秒...`);
        updateStatus(`${type}连接断开，正在尝试重新连接 (${newRetryCount}/${maxRetries})...`, 'connecting');

        setTimeout(() => {
            if (type === '视频') {
                connectWebSocketVideo(socketVideo.url);
            } else {
                connectWebSocketAudio(socketAudio.url);
            }
        }, retryInterval);
    }

    // 绑定启动按钮事件
    startButton.addEventListener('click', startChat);

    // 绑定重新连接按钮事件
    reconnectButton.addEventListener('click', () => {
        startChat();
    });

    // 切换麦克风
    toggleMicrophoneButton.addEventListener('click', () => {
        if (localStream) {
            const audioTracks = localStream.getAudioTracks();
            if (audioTracks.length === 0) {
                alert('当前没有麦克风设备。');
                return;
            }
            const enabled = audioTracks[0].enabled;
            audioTracks.forEach(track => track.enabled = !enabled);
            isLocalAudioPaused = enabled;
            updateToggleButtonState(toggleMicrophoneButton, !enabled, '🎤', '麦克风已开启。', '麦克风已关闭。');
        }
    });

    // 切换视频
    toggleVideoButton.addEventListener('click', () => {
        if (localStream) {
            const videoTracks = localStream.getVideoTracks();
            if (videoTracks.length === 0) {
                alert('当前没有摄像头设备。');
                return;
            }
            const enabled = videoTracks[0].enabled;
            videoTracks.forEach(track => track.enabled = !enabled);
            isLocalVideoPaused = enabled;
            updateToggleButtonState(toggleVideoButton, !enabled, '🎥', '视频已开启。', '视频已关闭。');
        }
    });

    // 静音功能
    muteButton.addEventListener('click', () => {
        isRemoteAudioMuted = !isRemoteAudioMuted;
        muteButton.classList.toggle('active', isRemoteAudioMuted);
        muteButton.classList.toggle('inactive', !isRemoteAudioMuted);
        muteButton.innerHTML = isRemoteAudioMuted ? '<span class="icon">🔇</span>取消静音' : '<span class="icon">🔊</span>静音';
        updateStatus(isRemoteAudioMuted ? '远端音频已静音。' : '远端音频已恢复。', 'info');
    });

    // 全屏功能
    fullscreenButton.addEventListener('click', () => {
        if (!document.fullscreenElement) {
            // 进入全屏
            videosContainer.requestFullscreen().catch(err => {
                console.error(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
                alert('无法进入全屏模式。');
            });
        } else {
            // 退出全屏
            document.exitFullscreen();
        }
    });

    // 监听全屏变化，更新按钮图标和文本
    document.addEventListener('fullscreenchange', () => {
        if (document.fullscreenElement) {
            // 添加全屏样式
            document.body.classList.add('fullscreen');
            fullscreenButton.innerHTML = '<span class="icon">🡼</span>退出全屏';
        } else {
            // 移除全屏样式
            document.body.classList.remove('fullscreen');
            fullscreenButton.innerHTML = '<span class="icon">⛶</span>全屏';
        }
    });

    // 更新切换按钮状态
    function updateToggleButtonState(button, isActive, icon, activeMessage, inactiveMessage) {
        if (isActive) {
            button.classList.remove('inactive');
            button.classList.add('active');
            // 显示当前状态的图标
            button.innerHTML = `<span class="icon">${icon}</span> ${activeMessage}`;
            updateStatus(activeMessage, 'info');
        } else {
            button.classList.remove('active');
            button.classList.add('inactive');
            // 显示当前状态的图标
            const inactiveIcon = icon === '🎤' ? '🔇' : '🎥';
            button.innerHTML = `<span class="icon">${inactiveIcon}</span> ${inactiveMessage}`;
            updateStatus(inactiveMessage, 'info');
        }
    }

    // 设置麦克风和视频按钮状态
    function setToggleButtonsState(isActive) {
        toggleMicrophoneButton.classList.remove('active', 'inactive');
        toggleVideoButton.classList.remove('active', 'inactive');

        if (isActive) {
            toggleMicrophoneButton.classList.add('active');
            toggleVideoButton.classList.add('active');
            toggleMicrophoneButton.innerHTML = `<span class="icon">🎤</span> 麦克风已开启`;
            toggleVideoButton.innerHTML = `<span class="icon">🎥</span> 视频已开启`;
        } else {
            toggleMicrophoneButton.classList.add('inactive');
            toggleVideoButton.classList.add('inactive');
            toggleMicrophoneButton.innerHTML = `<span class="icon">🔇</span> 麦克风已关闭`;
            toggleVideoButton.innerHTML = `<span class="icon">🎥</span> 视频已关闭`;
        }
    }

    // 清理资源
    window.addEventListener('beforeunload', () => {
        if (socketVideo) {
            socketVideo.close();
        }
        if (socketAudio) {
            socketAudio.close();
        }
        if (localStream) {
            localStream.getTracks().forEach(track => track.stop());
        }
        if (audioContext) {
            audioContext.close();
        }
    });

    // 监听网络状态变化
    window.addEventListener('online', () => {
        updateStatus('网络已连接。', 'info');
    });

    window.addEventListener('offline', () => {
        updateStatus('网络断开。', 'error');
    });

    // 初始化设备列表
    getAndPopulateDevices();
</script>
</body>
</html>
